import gugga.collections.ArrayList;

import test_framework.test.unit.TestCase;
import test_framework.env.reflect.ClassInfo;

/**
 * This is an utility class, that can help you to test asynchronous 
 * <code>ITask</code> classes or tasks which are using 
 * <code>dispatchEventLater</code> to distribute events. Basically 
 * <code>TaskExecutionEvaluator</code> contains two registers:
 * <ul>
 * 	<li><code>requiredTaskStatusRegister</code> register for the events that should be caught and consumed</li>
 * 	<li><code>taskStatusRegister</code> register for the actually caught and consumed events</li>
 * </ul>
 * <p>
 * <code>TaskExecutionEvaluator</code> can compare these registers 
 * synchronously and asynchronously. 
 * <p>
 * Basic usage:
 * <code>
 * 	<pre>
 * 	class SomeTaskTest extends TestCase
 * 	{
 * 		public function SomeTaskTest()
 * 		{
 * 			super();
 * 		}
 * 		
 * 		public function testAsyncTask()
 * 		{
 * 			// initialize TaskExecutionEvaluator and set delayed evaluation:
 * 			var evaluator:TaskExecutionEvaluator 
 * 				= new TaskExecutionEvaluator(this);
 * 			var delayInterval:Number = 1000;
 * 			evaluator.setDelayedEvaluation(delayInterval);
 * 			
 * 			// initialize asynchronous task:
 * 			var asyncTask:ITask = new SomeAsyncTask();
 * 			
 * 			asyncTask.addEventListener(
 * 					"start", 
 * 					Delegate.create(evaluator, evaluator.onTaskStarted));
 * 			asyncTask.addEventListener(
 * 					"completed", 
 * 					Delegate.create(evaluator, evaluator.onTaskCompleted));
 * 			
 * 			// start the asynchronous task:
 * 			asyncTask.start();
 * 			
 * 			// put in the requiredTaskStatusRegister the events 
 * 			// that should be caught and consumed:
 * 			evaluator.registerRequiredTaskStatus(
 * 					{task:preconditionsTask, event:"start"});
 * 			evaluator.registerRequiredTaskStatus(
 * 					{task:preconditionsTask, event:"completed"});
 * 			
 * 			// pause to check the taskStatusRegister:
 * 			evaluator.pauseToEvaluateTaskExecution();
 * 		}
 * 	}
 * 	</pre>
 * </code>
 * <p>
 * <b>IMPORTANT NOTICE!!!</b>
 * <p>
 * In future versions of the <code>guggaLibTests</code> 
 * <code>TaskExecutionEvaluator</code> should be used instead of 
 * inheriting <code>TaskTestCase</code>. Instantiating 
 * <code>DelayedTaskExecutionEvaluator</code> objects will give you the benefit 
 * of isolating the task registers in separate objects, so they can not 
 * interfere with one another.
 * 
 * @author vladimir
 */
class guggaLibTests.support.TaskExecutionEvaluator 
	extends TestCase
{
	private var delayIntervalID:Number;
	private var delayInterval:Number;
	
	private var paused:Boolean;
	
	private var taskStatusRegister:ArrayList;
	private var requiredTaskStatusRegister:ArrayList;
	
	private var parentTestCase:TestCase;
	
	public function TaskExecutionEvaluator(aParentTestCase:TestCase)
	{
		super();
		this.paused = false;
		this.parentTestCase = aParentTestCase;
		
		this.taskStatusRegister = new ArrayList();
		this.requiredTaskStatusRegister = new ArrayList();
	}
	
	public function setDelayedEvaluation(aDelayInterval)
	{
		this.delayInterval = aDelayInterval;
		this.delayIntervalID = setInterval(
				this, 
				this.EVALUATE_TASK_EXECUTION_METHOD_NAME, 
				this.delayInterval);
	}
	
	public function registerRequiredTaskStatus(aRequiredTaskStatus:Object)
	{
		this.requiredTaskStatusRegister.addItem(aRequiredTaskStatus);
	}
	
	public function reset()
	{
		this.paused = false;
		
		this.taskStatusRegister = new ArrayList();
		this.requiredTaskStatusRegister = new ArrayList();
	}
	
	public function onTaskStarted(aEvent:Object)
	{
		this.taskStatusRegister.addItem({task:aEvent.target, event:"start"});
	}
	
	public function onTaskCompleted(aEvent:Object)
	{
		this.taskStatusRegister.addItem({task:aEvent.target, event:"completed"});
	}
	
	public function onTaskInterrupted(aEvent:Object)
	{
		this.taskStatusRegister.addItem({task:aEvent.target, event:"interrupted"});
	}
	
	public function onTaskAdded(aEvent:Object)
	{
		this.taskStatusRegister.addItem({task:aEvent.target, event:"taskAdded"});
	}
	
	public function onTaskRemoved(aEvent:Object)
	{
		this.taskStatusRegister.addItem({task:aEvent.target, event:"taskRemoved"});
	}
	
	public function pauseToEvaluateTaskExecution()
	{
		this.paused = true;
		this.parentTestCase.pause();
	}
	
	private var EVALUATE_TASK_EXECUTION_METHOD_NAME:String 
		= "evaluateTaskExecution";
		
	public function evaluateTaskExecution()
	{
		clearInterval(this.delayIntervalID);
		
		for(var i = 0; i < requiredTaskStatusRegister.length; i++)
		{
			var taskInfo:ClassInfo 
				= ClassInfo.forInstance(requiredTaskStatusRegister[i].task);
			this.parentTestCase.assertTrue(
					"undefined task found - event source:" + taskInfo.getName() + "; expected status:" + requiredTaskStatusRegister[i].event + "(on iteration " + i + ")",
					this.taskStatusRegisterContains(requiredTaskStatusRegister[i]));
		}
		
		if(this.paused)
		{
			this.paused = false;
			this.parentTestCase.resume();
		}
	}
	
	public function assertTaskStatusRegisterEqualsRequiredTaskStatusRegister(
			aMessage:String) : Boolean
	{
		return this.parentTestCase.assertTrue(
				aMessage, 
				taskStatusRegister.length == requiredTaskStatusRegister.length);
	}
	
	public function assertTaskStatusRegisterIsEmpty(aMessage:String) : Boolean
	{
		return this.parentTestCase.assertTrue(aMessage, taskStatusRegister.isEmpty());
	}
	
	private function taskStatusRegisterContains(aRequiredTaskStatus:Object) : Boolean
	{
		for(var i = 0; i < taskStatusRegister.length; i++)
		{
			if(aRequiredTaskStatus.task == taskStatusRegister[i].task
				&& aRequiredTaskStatus.event == taskStatusRegister[i].event)
			{
				return true;
			}
		}
		
		return false;
	}
	
	public function printTaskStatusRegister()
	{
		var message:String = "TaskStatusRegister contents: ";
		
		for(var i = 0; i < taskStatusRegister.length; i++)
		{
			var taskInfo:ClassInfo 
				= ClassInfo.forInstance(taskStatusRegister[i].task);
			message += "\ntask: " + taskInfo.getName();
			message += "; status: " + taskStatusRegister[i].event + ";";
		}
		
		this.parentTestCase.assertTrue(message, false);
	}
}